home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / heap55.com / HEAPLOG.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1990-01-07  |  7.6 KB  |  312 lines

  1. {*****************************************************************************
  2.  This unit logs heap activity to disk. A report is automatically generated at
  3.  program startup and exit. Additional reports may be generated at any time by
  4.  calling DumpHeapLog.
  5.  
  6.  For further information, refer to HEAP.DOC.
  7.  
  8.  Copyright (C) TurboPower Software, 1989. All rights reserved.
  9.  May be distributed freely, but not commercially without express permission
  10.  of TurboPower Software.
  11.  
  12.  Version 5.0.
  13.    First release.
  14.  Version 5.5, 1/6/90
  15.    Modified to work with TP 5.5.
  16. *****************************************************************************}
  17.  
  18. {Define the following to have HEAPLOG report the FAR return address of each
  19.  caller to GetMem and FreeMem}
  20. {.$DEFINE AllocRet}
  21.  
  22. {$R-,S-,B-,F-,I-,V-}
  23.  
  24. unit HeapLog;
  25.   {-Keep a log of heap activity}
  26.  
  27. interface
  28.  
  29. uses
  30.   GrabHeap;
  31.  
  32. const
  33.   MaxLog = 1000;              {Maximum number of blocks allocated at once}
  34.   HeapLogName = 'HEAP.LOG';   {File name where log is written}
  35.  
  36. type
  37.   LogRec =
  38.     record
  39.       PtrVal : Pointer;       {Address of heap block}
  40.       AllocSize : Word;       {Bytes allocated}
  41.       AllocAt0 : Pointer;     {First return address of GetMem or New call}
  42.       {$IFDEF AllocRet}
  43.       AllocAt1 : Pointer;     {Next return address of GetMem or New call}
  44.       {$ENDIF}
  45.     end;
  46.   LogArray = array[1..MaxLog] of LogRec;
  47.  
  48. var
  49.   Log : ^LogArray;            {Log array stored on heap}
  50.   LogFilled : Boolean;        {Set true if simultaneous pointers exceed MaxLog}
  51.  
  52. function GetLog(Size : Word) : Pointer;
  53.   {-GetMem with logging}
  54.  
  55. procedure FreeLog(P : Pointer; Size : Word);
  56.   {-FreeMem with logging}
  57.  
  58. procedure DumpHeapLog(Msg : string);
  59.   {-Write the current heap log to a file}
  60.  
  61. procedure ClearLog;
  62.   {-Clear all entries from the log}
  63.  
  64.   {=================================================================}
  65.  
  66. implementation
  67.  
  68. const
  69.   Digits : array[0..$F] of Char = '0123456789ABCDEF';
  70. type
  71.   SO =
  72.     record
  73.       O, S : Word;
  74.     end;
  75.   FreeRec =
  76.     record
  77.       OrgPtr : Pointer;
  78.       EndPtr : Pointer;
  79.     end;
  80.   FreeList = array[0..8190] of FreeRec;
  81.   FreeListP = ^FreeList;
  82.  
  83. var
  84.   SaveExit : Pointer;
  85.  
  86.   function GetLog(Size : Word) : Pointer;
  87.     {-GetMem with logging}
  88.   type
  89.     StackRec =
  90.       record
  91.         DummyIndex : Word;
  92.         DummyP : Pointer;
  93.         DummyFunc : Pointer;
  94.         BP : Word;
  95.         RetAddr : Pointer;
  96.       end;
  97.   var
  98.     P : Pointer;
  99.     Index : Word;
  100.     Stack0 : StackRec absolute Index;
  101.   begin
  102.     {Let SYSTEM do the allocation}
  103.     SystemHeapControl;
  104.     GetMem(P, Size);
  105.     GetLog := P;
  106.  
  107.     CustomHeapControl(GetLog, FreeLog);
  108.  
  109.     {Find next free log record}
  110.     for Index := 1 to MaxLog do
  111.       with Log^[Index] do
  112.         if PtrVal = nil then begin
  113.           {Unused log entry}
  114.           PtrVal := P;
  115.           AllocSize := Size;
  116.           AllocAt0 := Stack0.RetAddr;
  117.  
  118.           {$IFDEF AllocRet}
  119.           if Stack0.BP <> 0 then
  120.             {AllocAt1 ASSUMES FIRST CALL WAS FAR!}
  121.             AllocAt1 := StackRec(Ptr(SSeg, Stack0.BP-2)^).RetAddr
  122.           else
  123.             AllocAt1 := nil;
  124.           {$ENDIF}
  125.  
  126.           Exit;
  127.         end;
  128.  
  129.     {Else log table full}
  130.     LogFilled := True;
  131.   end;
  132.  
  133.   procedure FreeLog(P : Pointer; Size : Word);
  134.     {-FreeMem with logging}
  135.   var
  136.     Index : Word;
  137.   begin
  138.     {Let SYSTEM do the deallocation}
  139.     SystemHeapControl;
  140.     FreeMem(P, Size);
  141.     CustomHeapControl(GetLog, FreeLog);
  142.  
  143.     {Find and free the log record}
  144.     for Index := 1 to MaxLog do
  145.       with Log^[Index] do
  146.         if PtrVal = P then begin
  147.           PtrVal := nil;
  148.           Exit;
  149.         end;
  150.   end;
  151.  
  152.   function HexW(W : Word) : string;
  153.     {-Return hex string for word}
  154.   begin
  155.     HexW[0] := #4;
  156.     HexW[1] := Digits[hi(W) shr 4];
  157.     HexW[2] := Digits[hi(W) and $F];
  158.     HexW[3] := Digits[lo(W) shr 4];
  159.     HexW[4] := Digits[lo(W) and $F];
  160.   end;
  161.  
  162.   function HexPtr(P : Pointer) : string;
  163.     {-Return hex string for pointer}
  164.   begin
  165.     HexPtr := HexW(SO(P).S)+':'+HexW(SO(P).O);
  166.   end;
  167.  
  168.   function FreeCount : Word;
  169.     {-Return the number of free list elements}
  170.   begin
  171.     if SO(FreePtr).O = 0 then
  172.       FreeCount := 0
  173.     else
  174.       FreeCount := ($10000-SO(FreePtr).O) shr 3;
  175.   end;
  176.  
  177.   function PtrDiff(H, L : Pointer) : LongInt;
  178.     {-Return the number of bytes between H^ and L^. H is the higher address}
  179.   begin
  180.     PtrDiff := ((LongInt(SO(H).S) shl 4+SO(H).O)-
  181.                 (LongInt(SO(L).S) shl 4+SO(L).O));
  182.   end;
  183.  
  184.   procedure DumpHeapLog(Msg : string);
  185.     {-Write the current heap log to a file}
  186.   var
  187.     Index : Word;
  188.     Count : Word;
  189.     FreeCnt : Word;
  190.     FP : FreeListP;
  191.     P0 : Pointer;
  192.     P1 : Pointer;
  193.     F : Text;
  194.   begin
  195.     Assign(F, HeapLogName);
  196.     Reset(F);
  197.     if IoResult = 0 then
  198.       {File already exists}
  199.       Append(F)
  200.     else
  201.       {New file}
  202.       Rewrite(F);
  203.     if IoResult <> 0 then
  204.       Exit;
  205.  
  206.     {Count the number of heap blocks allocated}
  207.     Count := 0;
  208.     for Index := 1 to MaxLog do
  209.       with Log^[Index] do
  210.         if PtrVal <> nil then
  211.           Inc(Count);
  212.     FreeCnt := FreeCount;
  213.  
  214.     {Write a message at the start of this dump}
  215.     WriteLn(F);
  216.     WriteLn(F, Msg);
  217.     WriteLn(F);
  218.     WriteLn(F, 'MemAvail: ', MemAvail);
  219.     WriteLn(F, 'MaxAvail: ', MaxAvail);
  220.     WriteLn(F, 'HeapPtr : ', HexPtr(HeapPtr));
  221.     WriteLn(F, 'HeapCnt : ', Count);
  222.     WriteLn(F, 'FreeCnt : ', FreeCnt);
  223.     WriteLn(F, 'Filled  : ', LogFilled);
  224.  
  225.     if Count <> 0 then begin
  226.       WriteLn(F);
  227.       WriteLn(F, '  Pointer   Size  Allocated at');
  228.       {           ssss:oooo  xxxxx  ssss:oooo  ssss:oooo}
  229.       for Index := 1 to MaxLog do
  230.         with Log^[Index] do
  231.           if PtrVal <> nil then begin
  232.             {Convert code addresses to relative format}
  233.             P0 := AllocAt0;
  234.             if P0 <> nil then
  235.               Dec(SO(P0).S, PrefixSeg+$10);
  236.             {$IFDEF AllocRet}
  237.             P1 := AllocAt1;
  238.             if P1 <> nil then
  239.               Dec(SO(P1).S, PrefixSeg+$10);
  240.             {$ENDIF}
  241.             WriteLn(F, HexPtr(PtrVal), '  ', AllocSize:5, '  ', HexPtr(P0)
  242.                     {$IFDEF AllocRet}
  243.                     ,'  ', HexPtr(P1)
  244.                     {$ENDIF}
  245.                     );
  246.           end;
  247.     end;
  248.  
  249.     if FreeCnt <> 0 then begin
  250.       {Write out the free list}
  251.       FP := FreePtr;
  252.       WriteLn(F);
  253.       WriteLn(F, 'Free start  Size');
  254.       {           ssss:oooo nnnnnn}
  255.       for Index := 0 to FreeCnt-1 do
  256.         with FP^[Index] do
  257.           WriteLn(F, HexPtr(OrgPtr), ' ', PtrDiff(EndPtr, OrgPtr):6);
  258.     end;
  259.  
  260.     Index := IoResult;
  261.     Close(F);
  262.     Index := IoResult;
  263.   end;
  264.  
  265.   procedure ClearLog;
  266.     {-Clear all entries from the log}
  267.   begin
  268.     LogFilled := False;
  269.     FillChar(Log^, SizeOf(LogArray), 0);
  270.   end;
  271.  
  272.   {$F+}
  273.   procedure ExitP;
  274.     {-Write the final log report}
  275.   begin
  276.     ExitProc := SaveExit;
  277.     DumpHeapLog('Final');
  278.   end;
  279.   {$F-}
  280.  
  281.   procedure DelLogFile;
  282.     {-Delete existing log file, if any}
  283.   var
  284.     I : Word;
  285.     F : file;
  286.   begin
  287.     Assign(F, HeapLogName);
  288.     Erase(F);
  289.     I := IoResult;
  290.   end;
  291.  
  292. begin
  293.   {Delete previous log file, if any}
  294.   DelLogFile;
  295.  
  296.   {Allocate the log array on the heap}
  297.   GetMem(Log, SizeOf(LogArray));
  298.  
  299.   {Clear out the log array}
  300.   ClearLog;
  301.  
  302.   {Take over heap allocation control}
  303.   CustomHeapControl(GetLog, FreeLog);
  304.  
  305.   {Set up to dump a final report}
  306.   SaveExit := ExitProc;
  307.   ExitProc := @ExitP;
  308.  
  309.   {Dump initial report}
  310.   DumpHeapLog('Initial');
  311. end.
  312.